package cn.edu.zust.se.service.file.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.edu.zust.se.api.clients.UserClient;
import cn.edu.zust.se.api.common.R;
import cn.edu.zust.se.api.entity.File;
import cn.edu.zust.se.api.entity.bo.FileChunkDTO;
import cn.edu.zust.se.api.entity.vo.FileChunkResultDTO;
import cn.edu.zust.se.api.entity.vo.FileVo;
import cn.edu.zust.se.api.entity.File;
import cn.edu.zust.se.api.entity.bo.FileBo;
import cn.edu.zust.se.api.entity.vo.FileVo;
import cn.edu.zust.se.api.entity.vo.UserVo;
import cn.edu.zust.se.service.file.commom.UpLoadConstant;
import cn.edu.zust.se.service.file.fastdfs.FastDFSService;
import cn.edu.zust.se.service.file.fastdfs.FastdfsConfig;
import cn.edu.zust.se.service.file.fastdfs.MultipartFileParam;
import cn.edu.zust.se.service.file.service.FileService;
import cn.hutool.core.bean.BeanUtil;
import cn.edu.zust.se.service.file.service.FolderService;
import cn.edu.zust.se.service.file.utils.ChunkUploadUtil;
import cn.edu.zust.se.service.file.utils.FastDFSClientUtil;
import cn.edu.zust.se.service.file.utils.RedisUtil;
import cn.edu.zust.se.service.file.utils.SimpleFileUtil;
import cn.hutool.core.io.FileUtil;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.exception.FdfsIOException;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/file")
@RequiredArgsConstructor
@Slf4j
public class FileController {
    //视频缓存时间,秒:1天
    private final Integer cacheTime = 60 * 60 * 24;
    private final FileService fileService;

    private final FolderService folderService;
    private final UserClient userClient;
    @Autowired
    AppendFileStorageClient appendFileStorageClient;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    FastDFSClientUtil fastDFSClientUtil;

    @Autowired
    private ChunkUploadUtil chunkUploadUtil;

    @Resource
    private FastDFSService fastDFSService;
    @Resource
    private FastdfsConfig fastdfsConfig ;
    private final StringRedisTemplate stringRedisTemplate;

    @GetMapping("/test")
    public void test() {
        R<UserVo> user = userClient.getUserById(1L);
        log.info(user.toString());
    }
    @PostMapping("/SimpleUpload")
    public String upload(MultipartFile file){
        return fastDFSService.uploadFile(file);
    }
    //根据文件夹id获取文件夹下的文件
    @GetMapping("/getFilesByFolderId/{folderId}/{type}")
    public List<File> getFilesByFolderId(@PathVariable Long folderId,@PathVariable Integer type) {
        return fileService.getFilesByFolderId(folderId,type);
    }

    //查询文件列表
    //type为1 代表正常文件，为2 代表回收站文件
    @GetMapping("/list/{type}")
    public R<List<FileVo>> list( FileBo fileBo, @PathVariable Integer type) {
        List<File> files = fileService.queryList(fileBo,type);
        List<FileVo> fileVos = BeanUtil.copyToList(files, FileVo.class);
        System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
        if (fileVos != null) {
            return R.ok(fileVos);
        }
        return R.fail("无法查询到文件列表");
    }

    /**
     * 上传文件(非分片上传)
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public R<FileVo> fdfsUpload(@RequestParam("file") MultipartFile file, Long folderId) {
        String fileUrl = "fail";
        try {
            fileUrl = fastDFSClientUtil.uploadFile(file);
            File uploadFile = new File();
            uploadFile.setPath(fileUrl);
            uploadFile.setOriginalName(file.getOriginalFilename());
            uploadFile.setSize(String.valueOf(file.getSize()));
            uploadFile.setType(SimpleFileUtil.getFileTypeCode(file));
            uploadFile.setFolderId(folderId);
            uploadFile.setUserId(StpUtil.getLoginIdAsLong());
            fileService.insertFile(uploadFile);
            FileVo fileVo = BeanUtil.copyProperties(uploadFile, FileVo.class);
            return R.ok(fileVo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.fail("上传失败");
    }

//    /**
//     * 本地存储
//     *
//     * @param file
//     * @return
//     */
//    @PostMapping(value = "/chunkUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
//    public R<Map> chunkUpload(MultipartFileParam file) {
//        System.out.println("***传输任务开始***");
//        System.out.println("fileName=" + file.getTaskId());
//        String fileUrl = "fail";
//        String path = "D:\\tmp";
//        Map<String, String> map = new HashMap<>();
//        map.put("path", fileUrl);
//        try {
//            fileUrl = chunkUploadUtil.chunkUploadByMappedByteBuffer(file, path);
//            map.put("path", fileUrl);
//        } catch (Exception e) {
//            e.printStackTrace();
//            return R.fail(map);
//        }
//        System.out.println("path=" + map.get("path"));
//        System.out.println("***传输任务结束***");
//        return R.ok(map);
//    }
//
//    /**
//     * fastdfs存储
//     *
//     * @param multipartFileParam
//     * @return
//     */
//    @PostMapping(value = "/fastDfsChunkUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
//    public R<Map> chunkUpload1(MultipartFileParam multipartFileParam, Long folderId) {
//        Map<String, String> map = new HashMap<>();
//        long chunk = multipartFileParam.getChunkNumber();
//        long totalChunk = multipartFileParam.getTotalChunks();
//        long chunkSize = multipartFileParam.getChunkSize();
//        long historyUpload = (chunk - 1) * chunkSize;
//        String md5 = multipartFileParam.getIdentifier();
//        MultipartFile file = multipartFileParam.getFile();
//        String fileName = FileUtil.extName(file.getOriginalFilename());
//        StorePath path = null;
//        String groundPath;
//
//        try {
//            if (chunk == 1) {
//                path = appendFileStorageClient.uploadAppenderFile(UpLoadConstant.DEFAULT_GROUP, file.getInputStream(),
//                        file.getSize(), fileName);
//                log.info("分片{}，上传至{}", chunk, path.getFullPath());
//                if (path == null) {
//                    map.put("result", "上传第一个就错了");
//                    return R.fail("上传第一个就错了");
//                } else {
//                    Boolean success = redisUtil.getLock(UpLoadConstant.uploadLock + md5);
//                    if (!success) {
//                        return R.fail("上传锁获取失败");
//                    }
//                    // 存储当前上传的分片
//                    stringRedisTemplate.opsForValue().set(UpLoadConstant.uploadChunkNum + md5, "1");
//                    stringRedisTemplate.expire(UpLoadConstant.uploadChunkNum + md5, cacheTime, TimeUnit.SECONDS);
//                    // 存储当前上传的分片路径
//                    stringRedisTemplate.opsForValue().set(UpLoadConstant.fastDfsPath + md5, path.getFullPath());
//                    stringRedisTemplate.expire(UpLoadConstant.fastDfsPath + md5, cacheTime, TimeUnit.SECONDS);
//                    // 存储已经上传的分片
//                    stringRedisTemplate.opsForSet().add(UpLoadConstant.uploaded + md5, String.valueOf(chunk));
//                    redisUtil.releaseLock(UpLoadConstant.uploadLock + md5);
//                }
//            } else {
//                log.info("当前md5为{}，分片为{}", md5, chunk);
//                log.info(UpLoadConstant.fastDfsPath+md5);
////                Boolean success = redisUtil.getLock(UpLoadConstant.uploadLock + md5);
////                if (!success) {
////                    return R.fail("上传锁获取失败");
////                }
//                Set<String> members = stringRedisTemplate.opsForSet().members(UpLoadConstant.uploaded + md5);
////                redisUtil.releaseLock(UpLoadConstant.uploadLock + md5);
//                if (members != null && members.contains(String.valueOf(chunk))) {
//                    log.info("分片{}已经上传过了", chunk);
//                    return R.ok(map);
//                }
//                // 判断当前分片是否是下一个合并的分片
//                Integer maxChunk = getMaxChunk(md5);
//                Integer waitSecond = 0;
//                while (chunk != maxChunk + 1 && waitSecond < 10) {
//                    log.info("当前分片{}，不是下一个合并的分片，等待", chunk);
//                    Thread.sleep(1000);
//                    waitSecond++;
//                    maxChunk = getMaxChunk(md5);
//                }
//                // 等待超时
//                if (waitSecond >= 10) {
//                    log.info("等待超时");
//                    return R.fail("等待超时");
//                }
//                String p = stringRedisTemplate.opsForValue().get(UpLoadConstant.fastDfsPath+md5);
//                log.info("当前路径为{}", p);
//                appendFileStorageClient.appendFile(UpLoadConstant.DEFAULT_GROUP, p, file.getInputStream(),
//                        file.getSize());
//                log.info("分片{}，上传至{}", chunk, p);
//                Integer chunkNum =Integer.parseInt(stringRedisTemplate.opsForValue().get(UpLoadConstant.uploadChunkNum + md5));
//                chunkNum = chunkNum + 1;
//                stringRedisTemplate.opsForValue().set(UpLoadConstant.uploadChunkNum + md5, String.valueOf(chunkNum));
//                stringRedisTemplate.expire(UpLoadConstant.uploadChunkNum + md5, cacheTime, TimeUnit.SECONDS);
//            }
//            Integer num = stringRedisTemplate.opsForSet().size(UpLoadConstant.uploaded + md5).intValue();
//            if (totalChunk == num) {
//                File uploadFile = new File();
//                uploadFile.setMd5(md5);
//                String resultUrl = fastdfsConfig.getOuturl() + path.getFullPath();
//                uploadFile.setPath(resultUrl);
//                uploadFile.setOriginalName(file.getOriginalFilename());
//                uploadFile.setSize(String.valueOf(totalChunk * chunkSize));
//                uploadFile.setType(SimpleFileUtil.getFileTypeCode(file));
//                uploadFile.setFolderId(folderId);
//                fileService.insertFile(uploadFile);
//                stringRedisTemplate.delete(UpLoadConstant.uploaded + md5);
//                stringRedisTemplate.delete(UpLoadConstant.uploadChunkNum + md5);
//                stringRedisTemplate.delete(UpLoadConstant.fastDfsPath + md5);
//            }
//            return R.ok(map);
//        } catch (FdfsIOException | SocketTimeoutException e) {
//            e.printStackTrace();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return R.fail(map);
//    }
//
//
//    /**
//     * 文件检查
//     *
//     * @param identifier
//     * @param folderId
//     * @return
//     */
//    @GetMapping("/fastDfsChunkUpload")
//    public R<FileVo> check(String identifier,Long folderId) {
//        FileVo file = fileService.check(identifier,folderId);
//        return R.ok(file);
//    }

//    /**
//     * 秒传
//     * @param fileId
//     * @param folderId
//     * @return
//     */
//    @PostMapping("/instantTransmit")
//    public R<Map> instantTransmit(Long fileId, Long folderId) {
//        Boolean success = fileService.instantTransmit(fileId, folderId);
//        if (success) {
//            return R.ok("文件传输成功");
//        }
//        return R.fail("文件传输失败");
//    }

    /**
     * 下载文件
     * @param fileId
     * @return
     */
    @GetMapping("/download/{fileId}")
    public ResponseEntity<byte[]> download(@PathVariable Long fileId) {
        try {
            return fileService.download(fileId);
        } catch (IOException e) {
            e.printStackTrace();
            // 在出现异常时返回适当的错误响应
            return ResponseEntity.status(500).body("Failed to download the file.".getBytes());
        }
    }


    //文件信息更新，仅用于文件重命名，文件移动,url更新（文件夹id为0代表最外层）
    @PutMapping("/updateFile")
    public R<Void> updateFile(@RequestBody FileBo fileBo) {
        File file = BeanUtil.copyProperties(fileBo, File.class);
        if (fileService.updateById(file)) {
            return R.ok();
        }
        return R.fail();
    }
    //文件删除
    //type为1 代表正常文件，为2 代表回收站文件
    @DeleteMapping("/deleteFile/{fileId}/{type}")
    public R<Void> deleteFile(@PathVariable Long fileId, @PathVariable Integer type) {
        File file = fileService.getById(fileId);
        if (type == 1) {
            file.setDelFlag(2);
            fileService.updateById(file);
            return R.ok();
        } else if (type == 2) {
            fileService.removeById(file);
            fastDFSClientUtil.delFile(file.getPath());
            return R.ok();
        }
        return R.fail();
    }
    //文件复制
    @PostMapping("/copyFile/{fileId}/{folderId}")
    public R<Void> copyFile(@PathVariable Long fileId, @PathVariable Long folderId) {
        if (fileService.copyFile(fileId, folderId)) {
            return R.ok();
        }
        return R.fail();
    }
    //文件详情查询
    @GetMapping("/getFileById/{fileId}")
    public R<File> getFileById(@PathVariable Long fileId) {
        File file = fileService.getById(fileId);
        if (file != null) {
            return R.ok(file);
        }
        return R.fail();
    }

    /**
     * 移动文件
     * @param fileBo
     * @return
     */
    @PutMapping("/moveFile")
    public R<Void> moveFile(@RequestBody FileBo fileBo) {
        if (fileService.move(fileBo)) {
            return R.ok();
        }
        return R.fail();
    }

    /**
     * 回收站文件，文件夹清空
     * @return
     */
    @DeleteMapping("")
    public R<Void> delete(){
        boolean login = StpUtil.isLogin();
        if (!login) {
            return R.fail("未登录");
        }
        long userId = Long.parseLong((String) StpUtil.getLoginId());
        Boolean success = fileService.clearAllByUserId(userId);
        if (success) {
            return R.ok();
        }
        return R.fail();
    }

    /**
     * 回收站文件删除
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public R<Void> delete(@PathVariable Long id){
        boolean login = StpUtil.isLogin();
        if (!login) {
            return R.fail("未登录");
        }
        long userId = Long.parseLong((String) StpUtil.getLoginId());
        File file = fileService.getById(id);
        if (file == null) {
            return R.fail("文件不存在");
        }
        if (file.getUserId() != userId) {
            return R.fail("无权删除");
        }
        Boolean success = fileService.clearFileById(id);
        if (success) {
            return R.ok();
        }
        return R.fail();
    }
    /**
     * 回收站文件还原
     * @param fileId
     * @return
     */
    @PutMapping("/recycle/{fileId}")
    public R<Void> recycle(@PathVariable Long fileId) {
        Boolean success = fileService.recycle(fileId);
        if (success) {
            return R.ok();
        }
        return R.fail();
    }

    /**
     * 检查分片是否存在
     *
     * @return
     */
    @GetMapping("chunk")
    public R<Object> checkChunkExist(FileChunkDTO chunkDTO) {
        FileChunkResultDTO fileChunkCheckDTO;
        try {
            fileChunkCheckDTO = fileService.checkChunkExist(chunkDTO);
            return R.ok(fileChunkCheckDTO);
        } catch (Exception e) {
            return R.fail(e.getMessage());
        }
    }


    /**
     * 上传文件分片
     *
     * @param chunkDTO
     * @return
     */
    @PostMapping("chunk")
    public R<Object> uploadChunk(FileChunkDTO chunkDTO) {
        try {
            fileService.uploadChunk(chunkDTO);
            return R.ok(chunkDTO.getIdentifier());
        } catch (Exception e) {
            return R.fail(e.getMessage());
        }
    }

    /**
     * 请求合并文件分片
     *
     * @param chunkDTO
     * @return
     */
    @PostMapping("merge")
    public R<Object> mergeChunks(@RequestBody FileChunkDTO chunkDTO,@RequestParam Long folderId) {
        log.info("请求合并文件分片");
        try {
            boolean success = fileService.mergeChunk(chunkDTO.getIdentifier(), chunkDTO.getFilename(), chunkDTO.getTotalChunks(),folderId);
            return success ? R.ok() : R.fail();
        } catch (Exception e) {
            return R.fail(e.getMessage());
        }
    }

    /**
     * 获取当前最大的分片
     */
    public Integer getMaxChunk(String md5) {
        Set<String> members = stringRedisTemplate.opsForSet().members(UpLoadConstant.uploaded + md5);
        if (members == null) {
            return 0;
        }
        return members.stream().max(String::compareTo).map(Integer::parseInt).orElse(0);
    }
    /*
      获取用户的根目录文件夹id
      @param userId
     */
    @GetMapping("/getRootFolderId/{userId}")
    public R<Long> getRootFolderId(@PathVariable Long userId) {
        return R.ok(fileService.getRootFolderId(userId));
    }

    //新增文件，用于注册时添加头像,返回文件id
    @PostMapping("/addFile")
    public R<Long> addFile(@RequestBody FileBo fileBo){
        File file = BeanUtil.copyProperties(fileBo, File.class);
        fileService.insertFile(file);
        return R.ok(file.getId());
    }


}
